
import { SHA1 } from './MySha1';
import { MD5 } from './MyMD5';
import { HMAC } from './hmac';
import { WordArray } from './lib-WordArray';
import { Hasher } from './AES/lib/Hasher';


export interface OptionalPBKDF2Config {
	keySize?: number;
	hasher?: Hasher;
	iterations?: number;
}

export interface PBKDF2Config extends OptionalPBKDF2Config {
	keySize: number;
	hasher: Hasher;
	iterations: number;
}
export class PBKDF2{
	public cfg: PBKDF2Config;
	constructor(cfg?: OptionalPBKDF2Config) {
		this.cfg = Object.assign({
			keySize: 128/32,
			hasher: SHA1,
			iterations: 1
		}, cfg);
	}
	public static create(cfg?: OptionalPBKDF2Config):PBKDF2{
		return new PBKDF2(cfg)
	}
	compute(password, salt):WordArray{
		// Shortcut
		var cfg = this.cfg;
		const hasher = new (<any> this.cfg.hasher)()
		// Init HMAC
		var hmac = HMAC.create(hasher, password);

		// Initial values
		var derivedKey = WordArray.create();
		var blockIndex = WordArray.create([0x00000001]);

		// Shortcuts
		var derivedKeyWords = derivedKey.words;
		var blockIndexWords = blockIndex.words;
		var keySize = cfg.keySize;
		var iterations = cfg.iterations;

		// Generate key
		while (derivedKeyWords.length < keySize) {
			var block = hmac.update(salt).finalize(blockIndex);
			hmac.reset();

			// Shortcuts
			var blockWords = block.words;
			var blockWordsLength = blockWords.length;

			// Iterations
			var intermediate = block;
			for (var i = 1; i < iterations; i++) {
				intermediate = hmac.finalize(intermediate);
				hmac.reset();

				// Shortcut
				var intermediateWords = intermediate.words;

				// XOR intermediate with block
				for (var j = 0; j < blockWordsLength; j++) {
					blockWords[j] ^= intermediateWords[j];
				}
			}

			derivedKey.concat(block);
			blockIndexWords[0]++;
		}
		derivedKey.sigBytes = keySize * 4;

		return derivedKey;
	}
}